//
// Created by wya on 2021/8/11.
//

#include "algorithm.h"

#include <algorithm>
#include <cstdlib>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <filesystem>
#include <sys/stat.h>
#include <unistd.h>
#include <ctime>
#include "opencv2/highgui.hpp"
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"

using namespace cv;
using namespace std;
namespace fs=boost::filesystem ;
/*
* TODO: This class is OS-specific; you might want to use Pointer-to-Implementation
* Idiom to hide the OS dependency from clients
*/
struct FileNameModificationDateComparator{
    //Returns true if and only if lhs < rhs
    bool operator() (const std::string& lhs, const std::string& rhs){
        struct stat attribLhs;
        struct stat attribRhs;  //File attribute structs
        stat( lhs.c_str(), &attribLhs);
        stat( rhs.c_str(), &attribRhs); //Get file stats
        return attribLhs.st_mtime < attribRhs.st_mtime; //Compare last modification dates
    }
};

int cmp_time(const std::string* lhs, const std::string* rhs)
{
    //Returns true if and only if lhs < rhs
    struct stat attribLhs;
    struct stat attribRhs;  //File attribute structs
    stat( lhs->c_str(), &attribLhs);
    stat( rhs->c_str(), &attribRhs); //Get file stats
    return attribLhs.st_mtime > attribRhs.st_mtime; //Compare last modification dates
};

struct cmp_name{
    bool operator() (const std::string& lhs, const std::string& rhs){
        //Returns true if and only if lhs < rhs
        if(stoi(lhs.substr(0,lhs.find('I'))) == stoi(rhs.substr(0,rhs.find('I')))){
            return stoi(lhs.substr(lhs.find('I')+1,lhs.rfind('.')-lhs.find('I'))) < stoi(rhs.substr(rhs.find('I')+1,rhs.rfind('.')-rhs.find('I')));
        }
        else return stoi(lhs.substr(0,lhs.find('I'))) < stoi(rhs.substr(0,rhs.find('I'))) ;
    };
}cmp;


int partition(vector<time_t> &a, int left, int right,std::vector<std::string> &b)
{
    int pivot = a[right];
    int i = left - 1;
    for (int j = left; j < right; j++)
    {
        if (a[j] <= pivot)
        {
            i++;
            swap(a[i], a[j]);
            swap(b[i], b[j]);
        }
    }
    swap(a[i + 1], a[right]);
    swap(b[i + 1], b[right]);
    return i + 1;
}

void quickSort(vector<time_t> &a, int left, int right,std::vector<std::string> &b)
{
    if (left < right)
    {
        int mid = partition(a, left, right,b);
        quickSort(a, left, mid - 1,b);
        quickSort(a, mid + 1, right,b);
    }
}

void qSort(vector<time_t> &a,std::vector<std::string> &b)
{
    quickSort(a, 0, a.size() - 1,b);
}


bool CopyDirectory(const std::string &strSourceDir, const std::string &strDestDir)
{


    boost::system::error_code ec;
    if(fs::is_directory(strSourceDir)){

        fs::recursive_directory_iterator end; //设置遍历结束标志，用recursive_directory_iterator即可循环的遍历目录
        for (fs::recursive_directory_iterator pos(strSourceDir); pos != end; ++pos)
        {
            //过滤掉目录和子目录为空的情况
            if(fs::is_directory(*pos))
            {
                continue;
            }

            std::string strAppPath = fs::path(*pos).string();
            std::string strRestorePath;
            //replace_first_copy在algorithm/string头文件中，在strAppPath中查找strSourceDir字符串，找到则用strDestDir替换，替换后的字符串保存在一个输出迭代器中
            boost::algorithm::replace_first_copy(std::back_inserter(strRestorePath), strAppPath, strSourceDir, strDestDir);
            if(!fs::exists(fs::path(strRestorePath).parent_path()))
            {
                fs::create_directories(fs::path(strRestorePath).parent_path(), ec);
                if(ec)
                {
                    std::cout<<"ERROR! Can not create the parent path: "<< fs::path(strRestorePath).parent_path() <<std::endl;
                    return false;
                }
            }
            fs::copy_file(strAppPath, strRestorePath, fs::copy_option::overwrite_if_exists, ec);
        }
    }
    else if(fs::is_regular_file(strSourceDir)){
        //复制文件

        if(!fs::exists(fs::path(strDestDir).parent_path()))
        {
            fs::create_directories(fs::path(strDestDir).parent_path(), ec);
            if(ec)
            {
                std::cout<<"ERROR! Can not create the parent path: "<< fs::path(strDestDir).parent_path() <<std::endl;
                return false;
            }
        }
        fs::copy_file(strSourceDir, strDestDir, fs::copy_option::overwrite_if_exists, ec);
    }
    return true;
}
////按文件最后修改时间进行升序排列
void Qsort_time(vector<string> & path){
    std::qsort (&path[0], path.size(), sizeof(path[0]) , reinterpret_cast<__compar_fn_t>(cmp_time));
}
////按文件名字进行升序排列
void sort_name( std::vector< std::string> & path){

    std::sort (path.begin(), path.end(), cmp);
}

/**
 * File_organization(const std::string &Src_path, const std::string &Dst_path)
 * 将Src_path下的文件按 Mono_7-27_10:52_Exp200Gain0 格式文件夹的修改时间进行排序存储，在Dst_path路径下按 Aolp Dolp QuadImages raw四个文件夹进行整理
 * Src_path 为包含 Mono_7-27_10:52_Exp200Gain0 格式目录的路径
 * Dst_path 为存储路径
**/
void File_organization(const std::string &Src_path, const std::string &Dst_path)
{
    fs::directory_iterator itend;
    vector<string> path;
    vector<time_t> time;

    for(fs::directory_iterator itor(Src_path);itor!=itend;++itor)
    {
        if(fs::is_directory(*itor))
        {
            path.push_back(itor->path().string());
            time.push_back(fs::last_write_time(*itor));
            continue;
        }
    }
    Qsort_time(path);
//    qSort(time,path);
    cout<<" ** Sorted path ** "<<endl;
    for (int k =0; k < path.size(); ++k)
    {
        cout << path[k]<<endl;
        for(fs::directory_iterator itor(path[k] );itor!=itend;++itor)
        {
            if(fs::is_directory(*itor))
            {
                string dir=itor->path().string().substr(itor->path().string().rfind('/')+1);
                if (dir=="Aolp") {
                    for(fs::directory_iterator Aolp(itor->path().string() );Aolp!=itend;++Aolp)
                    {
                        if(fs::is_regular_file(*Aolp))
                        {
                            string::size_type position;
                            position=Aolp->path().string().rfind("Heatmap");
                            if(position!=Aolp->path().string().npos)
                                CopyDirectory(Aolp->path().string(),Dst_path+"/Aolp/"+ to_string(k)+"_Heatmap.png");
                            else
                                CopyDirectory(Aolp->path().string(),Dst_path+"/Aolp/"+ to_string(k)+".png");
                        }
                        else cout<<"There is not regular file in 'Aolp' dir"<<endl;
                    }
                }
                else if (dir=="Dolp"){
                    for(fs::directory_iterator Dolp(itor->path().string() );Dolp!=itend;++Dolp)
                    {
                        if(fs::is_regular_file(*Dolp))
                        {
                            string::size_type position;
                            position=Dolp->path().string().rfind("Heatmap");
                            if(position!=Dolp->path().string().npos)
                                CopyDirectory(Dolp->path().string(),Dst_path+"/Dolp/"+ to_string(k)+"_Heatmap.png");
                            else
                                CopyDirectory(Dolp->path().string(),Dst_path+"/Dolp/"+ to_string(k)+".png");
                        }
                        else cout<<"There is not regular file in 'Dolp' dir"<<endl;
                    }

                }
                else if(dir=="QuadImages"){
                    for(fs::directory_iterator QuadImages(itor->path().string() );QuadImages!=itend;++QuadImages)
                    {
                        if(fs::is_regular_file(*QuadImages))
                        {
                            string::size_type position;
                            position=QuadImages->path().string().rfind("Heatmap");
                            if(position==QuadImages->path().string().npos)
                                CopyDirectory(QuadImages->path().string(),Dst_path+"/QuadImages/"+ to_string(k)
                                +QuadImages->path().string().substr(QuadImages->path().string().rfind('I')));

                        }
                        else cout<<"There is not regular file in 'QuadImages' dir"<<endl;
                    }
                }
                else if(dir=="raw"){
                    for(fs::directory_iterator raw(itor->path().string() );raw!=itend;++raw)
                    {
                        if(fs::is_regular_file(*raw))
                        {
                            string::size_type position;
                            position=raw->path().string().rfind("Heatmap");
                            if(position!=raw->path().string().npos)
                                CopyDirectory(raw->path().string(),Dst_path+"/raw/"+ to_string(k)+"_Heatmap.png");
                            else
                                CopyDirectory(raw->path().string(),Dst_path+"/raw/"+ to_string(k)+".png");
                        }
                        else cout<<"There is not regular file in 'raw' dir"<<endl;
                    }
                }
                else cout<<"There is not 'Aolp' 'Dolp' 'QuadImages' 'raw' directory"<<endl;
            }
        }
    }

}


/**
 * Averageimage(const std::string &Dst_path)
 * 将Dst_path下QuadImages目录里的polarization images按 I0 I45 I90 I135加和取平均图像，并存储在Dst_path路径下。
 * Dst_path 为包含QuadImages目录的路径
**/
void Averageimage(const std::string &Dst_path)
{
    vector<string> fileV;
    for(auto const & file: fs::directory_iterator(Dst_path+"/QuadImages"))
    {
        fileV.push_back(file.path().string().substr(file.path().string().rfind('/')+1));
    }

    sort_name(fileV);

    if(!fs::exists(Dst_path+"/AverageImages"))
        fs::create_directory(Dst_path+"/AverageImages");

    Mat img0= imread(Dst_path+"/QuadImages/"+fileV[0],IMREAD_UNCHANGED);
    Mat AverageImage =Mat::zeros(img0.rows,img0.cols, CV_32F);
    accumulate(img0, AverageImage);
    int a=0;
    string nums;
    stringstream name;
    for (int i = 1; i < fileV.size(); ++i) {
        cout<<fileV[i]<<endl;
        Mat img= imread(Dst_path+"/QuadImages/"+fileV[i],IMREAD_UNCHANGED);
        accumulate(img, AverageImage);
        if((i+1)%4==0){
            name.clear();
            name.str("");
            name<<std::setw(6)<<setiosflags(std::ios::right)<<std::setfill('0')<<std::to_string(a);
            name>>nums;

            AverageImage/=4;
            AverageImage.convertTo(AverageImage, CV_8U);
            imwrite(Dst_path+"/AverageImages/"+ nums+".png",AverageImage);
            a++;
            AverageImage=Mat::zeros(img0.rows,img0.cols, CV_32F);
        }
    }

}
/**
 * Data_collation(const std::string &Src_path, const std::string &Dst_path)
 * 将Src_path下的文件按 Mono_7-27_10:52_Exp200Gain0 格式文件夹的修改时间进行排序存储，在Dst_path路径下按 Aolp Dolp QuadImages raw四个文件夹进行整理
 * Src_path 为包含 Mono_7-27_10:52_Exp200Gain0 格式目录的路径
 * Dst_path 为空白的存储路径，会生成 Aolp Dolp QuadImages raw四个文件夹
 * 将Dst_path下QuadImages目录里的polarization images按 I0 I45 I90 I135加和取平均图像，并存储在Dst_path/Averageimage/路径下。
**/
void Data_collation(const std::string &Src_path, const std::string &Dst_path)
{
    File_organization(Src_path,Dst_path);
    Averageimage(Dst_path);
}